//
// MetaTag.swift
// Ignite
// https://www.github.com/twostraws/Ignite
// See LICENSE for license information.
//

import Foundation

/// An item of metadata that helps browsers and search engines understand
/// your page better.
public struct MetaTag: HeadElement, Sendable {
    /// Metadata names for social media cards.
    public enum `Type`: String, Sendable {
        /// The image to display in Twitter cards.
        case twitterImage = "twitter:image"
        /// The title to display in Twitter cards.
        case twitterTitle = "twitter:title"
        /// The domain to display in Twitter cards.
        case twitterDomain = "twitter:domain"
        /// The type of Twitter card to use.
        case twitterCard = "twitter:card"
        /// Whether to disable Twitter tracking.
        case twitterDoNotTrack = "twitter:dnt"
        /// The description to display in Twitter cards.
        case twitterDescription = "twitter:description"
        /// The software used to generate the document.
        case generator
        /// The viewport configuration for responsive web design.
        case viewport

        /// The title of the shared content.
        case openGraphTitle = "og:title"
        /// A description of the shared content.
        case openGraphDescription = "og:description"
        /// The image to display when sharing.
        case openGraphImage = "og:image"
        /// The canonical URL of the shared content.
        case openGraphURL = "og:url"
        /// The name of the website.
        case openGraphSiteName = "og:site_name"

        /// The meta attribute key.
        var key: String {
            switch self {
            case .twitterImage, .twitterTitle, .twitterDomain, .twitterCard,
                 .twitterDoNotTrack, .twitterDescription, .generator, .viewport:
                "name"
            case .openGraphTitle, .openGraphDescription, .openGraphImage,
                 .openGraphURL, .openGraphSiteName:
                "property"
            }
        }
    }

    /// Allows mobile browsers to scale this page appropriately for the device.
    public static let flexibleViewport = MetaTag(.viewport, content: "width=device-width, initial-scale=1")

    /// Marks this page as using UTF-8 content.
    public static let utf8 = MetaTag(characterSet: "utf-8")

    /// Marks this page as having been automatically generated by Ignite.
    public static let generator = MetaTag(.generator, content: Ignite.version)

    /// The standard set of control attributes for HTML elements.
    public var attributes = CoreAttributes()

    /// A convenience initializer for when you want a piece of data with a
    /// URL as its content. Creates a new `MetaTag` instance from the type
    /// and URL provided.
    /// - Parameters:
    ///   - type: The type of metadata.
    ///   - content: The URL value for the metadata.
    public init(_ type: `Type`, content: URL) {
        self.init(type, content: content.absoluteString)
    }

    /// Creates a new `MetaTag` instance from a metadata type and content provided.
    /// - Parameters:
    ///   - type: The type of metadata.
    ///   - content: The value for the metadata.
    public init(_ type: `Type`, content: String) {
        attributes.append(customAttributes: .init(name: type.key, value: type.rawValue))
        attributes.append(customAttributes: .init(name: "content", value: content))
    }

    /// Creates a new `MetaTag` instance from the custom name and content provided.
    /// - Parameters:
    ///   - name: The name for the metadata.
    ///   - content: The value for the metadata.
    public init(name: String, content: String) {
        attributes.append(customAttributes: .init(name: "name", value: name))
        attributes.append(customAttributes: .init(name: "content", value: content))
    }

    /// Creates a new `MetaTag` instance from the custom property and content provided.
    /// - Parameters:
    ///   - property: The property name for the metadata.
    ///   - content: The value for the metadata.
    public init(property: String, content: String) {
        attributes.append(customAttributes: .init(name: "property", value: property))
        attributes.append(customAttributes: .init(name: "content", value: content))
    }

    /// Creates a new piece of metadata that sets a specific character set for
    /// the current page.
    /// - Parameter characterSet: The value for the character set.
    public init(characterSet: String) {
        attributes.append(customAttributes: .init(name: "charset", value: characterSet))
    }

    /// Creates a new `MetaTag` instance for HTTP equivalent headers.
    /// - Parameters:
    ///   - httpEquivalent: The HTTP header to simulate
    ///   - content: The value for the header
    public init(httpEquivalent: String, content: String) {
        attributes.append(customAttributes: .init(name: "http-equiv", value: httpEquivalent))
        attributes.append(customAttributes: .init(name: "content", value: content))
    }

    /// Returns a standard set of social sharing metadata for a given page,
    /// including the page title, description, and image.
    /// - Returns: An array of `MetaTag` objects that should be placed
    /// into your page header to enable social sharing.
    @ElementBuilder<MetaTag> public static func socialSharingTags() -> [MetaTag] {
        let site = PublishingContext.shared.site
        let environment = PublishingContext.shared.environment

        MetaTag(.openGraphSiteName, content: site.name)

        if let image = environment.page.image {
            MetaTag(.openGraphImage, content: image)
            MetaTag(.twitterImage, content: image)
        }

        let pageTitle = environment.page.title
        MetaTag(.openGraphTitle, content: pageTitle)
        MetaTag(.twitterTitle, content: pageTitle)

        let pageDescription = environment.page.description
        if pageDescription.isEmpty == false {
            MetaTag(.openGraphDescription, content: pageDescription)
            MetaTag(.twitterDescription, content: pageDescription)
        }

        let pageURL = environment.page.url
        MetaTag(.openGraphURL, content: pageURL)

        if let domain = pageURL.removingWWW {
            MetaTag(.twitterDomain, content: domain)
        }

        MetaTag(.twitterCard, content: "summary_large_image")
        MetaTag(.twitterDoNotTrack, content: "on")
    }

    /// Renders this element using publishing context passed in.
    /// - Returns: The HTML for this element.
    public func markup() -> Markup {
        Markup("<meta\(attributes) />")
    }
}

public extension MetaTag {
    /// Adds a data attribute to the element.
    /// - Parameters:
    ///   - name: The name of the data attribute
    ///   - value: The value of the data attribute
    /// - Returns: The modified `MetaTag`
    func data(_ name: String, _ value: String) -> Self {
        var copy = self
        copy.attributes.data.append(.init(name: name, value: value))
        return copy
    }

    /// Adds a custom attribute to the element.
    /// - Parameters:
    ///   - name: The name of the custom attribute
    ///   - value: The value of the custom attribute
    /// - Returns: The modified `HTML` element
    func customAttribute(name: String, value: String) -> Self {
        var copy = self
        copy.attributes.append(customAttributes: .init(name: name, value: value))
        return copy
    }
}
